﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace UmlControlLibrary
{
    /// <summary>
    /// Interaction logic for DraggableLabel.xaml
    /// </summary>
    public partial class DraggableLabel : SelectableItemControl, IDraggable
    {
        public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(DraggableLabel));
        public static DependencyProperty IsDraggableProperty = DependencyProperty.Register("IsDraggable", typeof(bool), typeof(DraggableLabel), new PropertyMetadata(true));
        public static DependencyProperty MultilineProperty = DependencyProperty.Register("Multiline", typeof(bool), typeof(DraggableLabel), new PropertyMetadata(false));
        public static RoutedEvent BeforeEditEvent = EventManager.RegisterRoutedEvent("BeforeEdit", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(DraggableLabel));
        public static RoutedEvent AfterEditEvent = EventManager.RegisterRoutedEvent("AfterEdit", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(DraggableLabel));

        public static TextBox SelectedTextBox = null;

        public DraggableLabel()
        {
          //  IsDraggable = true;
            InitializeComponent();
            AddDragZone(Caption);
        }

        public event RoutedEventHandler BeforeEdit
        {
            add { AddHandler(BeforeEditEvent, value); }
            remove { RemoveHandler(BeforeEditEvent, value); }
        }

        protected virtual void OnBeforeEdit()
        {
            if (BeforeEditEvent != null)
            {
                RoutedEventArgs args = new RoutedEventArgs();
                args.RoutedEvent = BeforeEditEvent;
                RaiseEvent(args);
            }
        }

        public event RoutedEventHandler AfterEdit
        {
            add { AddHandler(AfterEditEvent, value); }
            remove { RemoveHandler(AfterEditEvent, value); }
        }

        protected virtual void OnAfterEdit()
        {
            if (AfterEditEvent != null)
            {
                RoutedEventArgs args = new RoutedEventArgs();
                args.RoutedEvent = AfterEditEvent;
                RaiseEvent(args);
            }
        }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public bool IsDraggable
        {
            get { return (bool)GetValue(IsDraggableProperty); }
            set { SetValue(IsDraggableProperty, value); }
        }

        public bool Multiline
        {
            get { return (bool)GetValue(MultilineProperty); }
            set { SetValue(MultilineProperty, value); }
        }

        public void OnOwnerDeselect(object sender)
        {
            Deselect();
        }

        #region SelectableItemControl overrides

        public override void Select()
        {
            base.Select();
            Canvas parent = this.Parent as Canvas;
            if (parent != null)
            {
                parent.Children.Remove(this);
                parent.Children.Add(this);
            }

            FrameworkElement element = (FrameworkElement)GetTemplateElement(Caption, "SelectedDecorator");
            element.Visibility = Visibility.Visible;
        }

        public override void Deselect()
        {
            base.Deselect();
            FrameworkElement element = (FrameworkElement)GetTemplateElement(Caption, "SelectedDecorator");
            element.Visibility = Visibility.Hidden;
            if (SelectedTextBox != null)
                DeselectTextBox();
        }

        #endregion

        #region IDraggable Members

        public void Drag(double deltaX, double deltaY)
        {
            if (IsDraggable)
            {
                double x = Canvas.GetLeft(this);
                double y = Canvas.GetTop(this);
                double left = AlignToGrid(Math.Max(x + deltaX, 0));
                double top = AlignToGrid(Math.Max(y + deltaY, 0));
                Canvas.SetLeft(this, left);
                Canvas.SetTop(this, top);
                this.Moved();
            }
        }

        #endregion

        #region Editable labels management

        private TextBox GetTemplatedTextBox()
        {
            return GetTemplateElement(this.Caption, "textBox") as TextBox;
        }

        private void ActivateTextBox(bool activate)
        {
            TextBox tb = GetTemplatedTextBox();
            tb.Visibility = activate ? Visibility.Visible : Visibility.Collapsed;

            if (activate)
            {
                OnBeforeEdit();
                UpdateFromSource();
                SelectedTextBox = tb;
            }
        }

        private void EditLabel()
        {
            Select();
            ActivateTextBox(true);
            TextBox tb = GetTemplatedTextBox();
            tb.Focus();
            tb.SelectAll();
        }

        private void DeselectTextBox()
        {
            ValidateTextboxChanges();
            ActivateTextBox(false);
        }

        private void UpdateFromSource()
        {
            TextBox tb = GetTemplatedTextBox();
            BindingExpression tbExpr = tb.GetBindingExpression(TextBox.TextProperty);
            BindingExpression bindingExpr = this.GetBindingExpression(DraggableLabel.TextProperty);
            if ((bindingExpr != null) && (bindingExpr.DataItem != null))
                bindingExpr.UpdateTarget();

            tbExpr.UpdateTarget();
        }

        private void ValidateTextboxChanges()
        {
            // Changes are commited only when the textbox lose focus, which doesn't happen.
            // (took WEEKS to figure this out)
            TextBlock l = GetTemplateElement(this.Caption, "label") as TextBlock;
            l.Focusable = true;
            bool r = l.Focus();
            l.Focusable = false;
            OnAfterEdit();
            UpdateFromSource();
        }

        private void CancelTextboxChanges()
        {
            OnAfterEdit();
            UpdateFromSource();
        }

        #endregion

        #region Textbox events
        private void textBox_LostFocus(object sender, RoutedEventArgs e)
        {
            if (sender is TextBox)
                DeselectTextBox();
        }

        private void textBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Return)
            {
                ValidateTextboxChanges();
                ActivateTextBox(false);
                if (!Selectable)
                    Deselect();
            }
            else if (e.Key == Key.Escape)
            {
                CancelTextboxChanges();
                ActivateTextBox(false);
                if (!Selectable)
                    Deselect();
            }
        }

        private void textBox_SourceUpdated(object sender, DataTransferEventArgs e)
        {
        }
        #endregion

        #region Mouse events
        private void label_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
                EditLabel();
        }
        #endregion
    }
}

